unit DUnitX.NUnit.Logger.Solution;
interface
uses MLC.UnitTestFramework, SBD.ServiceProvider, DUnitX.NUnit, SBD.Messages;

type
TNUnit25_Logger = class( TInterfacedObject, ITestLogger)
  private
    [Injection] FFilenameSource: IFilenameSource;
    [Injection] FNUnit25DomainLogger: INUnit25_ResultsLog;
    FDescription: string;
    FFilename: string;

    function  MenuDescription: string;
    procedure OnEnableChange( const Eng: IUnitTestingEngine; const Proc: ITestProcedure; const Datum: IInterface);
    procedure OnBeginRunInstance( Workload: integer);
    procedure OnEndRunInstance( const RunResult: ITestResult);
    procedure OnChangeStatus(
                        NodeKind: TLoggerNode;
                  const Fixture: ITestFixture;
                  const TestProc: ITestProcedure;
                  const TestCase: ITestCase;
                  const TestResult: ITestResult;
                  const NodeDatum: IInterface);
    procedure OnProgressTick( const GrossResult: ITestResult; WorkDone, WorkLoad: integer);
    procedure Breathe;
    procedure OnAuxilaryMessage( const TestResult: ITestResult; const TestCase: ITestCase; const Msg: RMessage; const NodeDatum: IInterface);
    procedure Shutdown( const Eng: IUnitTestingEngine);
    procedure OnAttachSuite( Suite: ITestSuite;
             SetFixtureDatum: TSetFixtureDatumFunc;
             SetTestProcDatum: TSetTestProcDatumFunc;
             SetTestCaseDatum: TSetTestCaseDatumFunc);
  public
    [Configuration('NUnit2.5')] constructor ServiceModeCreate;
  end;

TLoggerFactory = class( TInterfacedObject, ILoggerFactory, IFilenameSource)
  private
    [Injection('NUnit2.5')] FLoggerProvider: IServiceProvider;
    FParams: IInterface;
    function MenuDescription: string;
    function CreateLogger( const Params: IInterface): ITestLogger;
    function FileName: string;
  public
    [Configuration('NUnit2.5')] constructor ServiceModeCreate;
  end;


procedure RegisterServices( const Provider: IServiceProvider);

implementation






uses IOUtils, SysUtils, Classes, SBD.Generics;

procedure RegisterServices( const Provider: IServiceProvider);
begin
Provider.RegisterServiceClass( ITestLogger, TNUnit25_Logger);
Provider.RegisterServiceClass( ILoggerFactory, TLoggerFactory);
end;


procedure TNUnit25_Logger.Breathe;
begin
end;

function TNUnit25_Logger.MenuDescription: string;
begin
result := FDescription
end;

procedure TNUnit25_Logger.OnAttachSuite(Suite: ITestSuite;
  SetFixtureDatum: TSetFixtureDatumFunc;
  SetTestProcDatum: TSetTestProcDatumFunc;
  SetTestCaseDatum: TSetTestCaseDatumFunc);
begin
end;

procedure TNUnit25_Logger.OnAuxilaryMessage(
  const TestResult: ITestResult; const TestCase: ITestCase; const Msg: RMessage; const NodeDatum: IInterface);
begin
end;

procedure TNUnit25_Logger.OnBeginRunInstance( Workload: integer);
begin
end;

procedure TNUnit25_Logger.OnChangeStatus(
  NodeKind: TLoggerNode;
  const Fixture: ITestFixture; const TestProc: ITestProcedure;
  const TestCase: ITestCase; const TestResult: ITestResult;
  const NodeDatum: IInterface);
begin
end;

procedure TNUnit25_Logger.OnEnableChange(
  const Eng: IUnitTestingEngine; const Proc: ITestProcedure; const Datum: IInterface);
begin
end;

procedure TNUnit25_Logger.OnEndRunInstance( const RunResult: ITestResult);
var
  Source: TStream;
  NUnit25: INUnit25_TestSuite;
  Total, Errored, NotRun, Ignored, Skipped: integer;
  GrossDuration: double;
  NUnitRunResult: TNUnitResult;

  procedure ParseResults( ParentSuite: INUnit25_TestSuite; const Results: ISEnumerable<ITestResult>);
  var
    NUnitRunResult: TNUnitResult;
    TestResult: ITestResult;
    Suite: INUnit25_TestSuite;
    Duration: double;
    Reason: string;
    Msg: RMessage;

  begin
  for TestResult in Results do
    begin
    Duration := TestResult.Timing.FDuration_ms / (1000 * SecsPerDay);
    case TestResult.Status of
      tsError, tsSetup, tsTeardown, tsExecuting: NUnitRunResult := rError;
      tsNeutral, tsSkipped:                      NUnitRunResult := rIgnored;
      tsPass, tsWarn:                            NUnitRunResult := rSuccess;
      tsFail:                                    NUnitRunResult := rFailure;
      end;
    if TestResult.ErrorOrFailureMsg( Msg) then
        Reason := Msg.FDisplayText
      else
        Reason := '';
    case TestResult.NodeKind of
      rnNamespaceFixture:
        Suite := ParentSuite.AddNamespaceSuite(
          TestResult.Fixture.Name, TestResult.Executed, NUnitRunResult,
          Duration, 0);
      rnLeafFixture:
        Suite := ParentSuite.AddFixtureSuite(
          TestResult.Fixture.Name, TestResult.Fixture.Description,
          TestResult.Executed, NUnitRunResult, Duration, 0, Reason);
      rnTestCase:
        begin
        Suite := nil;
        ParentSuite.AddTestCase(
          TestResult.TestCase.DisplayName, TestResult.TestCase.Parent.Description,
          TestResult.Executed, NUnitRunResult, Duration, 0, Reason)
        end
      end;
    if assigned( Suite) then
      ParseResults( Suite, TestResult.ChildResults)
    end;
  ParentSuite.Close
  end;

begin
try
  Source := TFile.Open( FFilename, IOUtils.TFileMode.fmOpenOrCreate);
  Source.Size := 0
except on E: Exception do
    // TODO:
    //  How to handle file i/o error?
  end;
Errored := RunResult.ErrorCount;
Total   := RunResult.PassCount + RunResult.FailCount + RunResult.WarnCount;
NotRun  := 0; // To be developed
Ignored := 0; // To be developed
Skipped := 0; // To be developed
if Errored > 0 then
    NUnitRunResult := rError
  else if RunResult.FailCount > 0 then
    NUnitRunResult := rFailure
  else if (RunResult.PassCount + RunResult.WarnCount) > 0 then
    NUnitRunResult := rSuccess
  else
    NUnitRunResult := rIgnored;
GrossDuration := RunResult.Timing.FDuration_ms / (1000 * SecsPerDay);
NUnit25 := FNUnit25DomainLogger.OpenLog( Source, ParamStr( 0), RunResult.Timing.FStart,
      Total, RunResult.FailCount, Errored, NotRun, Ignored, Skipped, NUnitRunResult, GrossDuration);
ParseResults( NUnit25, RunResult.ChildResults)
end;

procedure TNUnit25_Logger.OnProgressTick(
  const GrossResult: ITestResult; WorkDone, WorkLoad: integer);
begin
end;

constructor TNUnit25_Logger.ServiceModeCreate;
begin
if assigned( FFilenameSource) then
    begin
    FFilename := FFilenameSource.FileName;
    FFilenameSource := nil
    end
  else
    FFilename := '';
if FFilename = '' then
  FFilename :=  TPath.ChangeExtension( ParamStr( 0), '.log');
FDescription := Format( 'NUnit 2.5 log at %s', [TPath.GetFileName( FFilename)])
end;

procedure TNUnit25_Logger.Shutdown( const Eng: IUnitTestingEngine);
begin
end;

{ TLoggerFactory }

function TLoggerFactory.CreateLogger( const Params: IInterface): ITestLogger;
var
  InjectionOverride: IServiceProvider;
begin
FParams := Params;
InjectionOverride := StandardServiceProvider;
InjectionOverride.RegisterServiceClass( IFilenameSource, self.ClassType);
FLoggerProvider.Gn.Acquire<ITestLogger>( nil, result, 'NUnit2.5', InjectionOverride);
InjectionOverride.ShutDown;
FParams := nil
end;

function TLoggerFactory.FileName: string;
var
  FNParam: IFilenameFactoryParameter;
begin
if Supports( FParams, IFilenameFactoryParameter, FNParam) then
    result := FNParam.FileName
  else
    result := ''
end;

function TLoggerFactory.MenuDescription: string;
begin
result := 'NUnit v2.5'
end;

constructor TLoggerFactory.ServiceModeCreate;
begin
end;

end.
